/*
 * Javlov - a Java toolkit for reinforcement learning with multi-agent support.
 * 
 * Copyright (c) 2009 Matthijs Snel
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.javlov.world;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.javlov.util.ObjectFactory;
import net.javlov.util.SAXReader;

import org.jdom.Element;

public class XMLAgentBodyBuilder implements AgentBodyBuilder {

	//TODO some of this code is very similar to XMLWorldBuilder
	
	protected List<AgentBody> bodies;
	private Iterator<AgentBody> bodyItr;
	protected World world;
	
	@Override
	public AgentBody buildBody() {
		if ( bodyItr.hasNext() )
			return bodyItr.next();
		return null;
	}
	
	public void load(String configFile) {
		//TODO handle null case
		Element root = SAXReader.read(configFile).getRootElement();
		List<Element> agents = root.getChildren("agent");
		bodies = new ArrayList<AgentBody>();
		int number;
		for ( Element el : agents ) {
			number = Integer.parseInt(el.getAttributeValue("number"));
			loadBodies( el.getChild("body"), number );
		}
		
		bodyItr = bodies.iterator();
	}
	
	public void setWorld(World w) {
		world = w;
	}
	
	protected void loadBodies(Element bodyEl, int number) {
		AgentBody b;
		String onInit = bodyEl.getAttributeValue("oninit"),
				onReset = bodyEl.getAttributeValue("onreset");
		String	x = bodyEl.getAttributeValue("x"),
				y = bodyEl.getAttributeValue("y"),
				width = bodyEl.getAttributeValue("width"),
				height = bodyEl.getAttributeValue("height");
		System.out.println(bodyEl.getAttributes());
		for ( int i = 0; i < number; i++ ) {
			b = (AgentBody) ObjectFactory.createObject(bodyEl.getChild("javaclass"));
			loadSensors(b, bodyEl);
			configBody(bodyEl, b, onInit, onReset, x, y, width, height);
			b.setType(Body.AGENT);
			bodies.add(b);
		}
	}
	
	protected void loadSensors(AgentBody b, Element bodyEl) {
		Element sensorClass;
		List<Sensor> sensors;
		
		//Composite sensors
		List<Element> sensorEls = bodyEl.getChildren("sensorgroup");
		for ( Element sensorEl : sensorEls ) {
			CompositeSensor cs;
			sensorClass = sensorEl.getChild("javaclass");
			if ( sensorClass != null )
				cs = (CompositeSensor) ObjectFactory.createObject(sensorClass);
			else
				cs = new CompositeSensor();
			
			sensors = getSensors(sensorEl);
			GroupedSensor gs;
			for ( int i = 0; i < sensors.size(); i++ ) {
				gs = (GroupedSensor) sensors.get(i);
				gs.setBody(b);
				cs.add(gs);
			}
			cs.setWorld(world);
			b.add(cs);
		}
		
		//all other sensors
		sensors = getSensors(bodyEl);
		for ( Sensor s : sensors ) {
			s.setWorld(world);
			b.add(s);
			s.setBody(b);
		}
	}
	
	@SuppressWarnings("unchecked")
	protected List<Sensor> getSensors(Element parentEl) {
		List<Element> childSensorEls = parentEl.getChildren("sensor");
		List<Sensor> sensors = new ArrayList<Sensor>(childSensorEls.size());
		Sensor s;
		for ( Element childSensorEl : childSensorEls ) {
			s = (Sensor) ObjectFactory.createObject(childSensorEl.getChild("javaclass"));
			if ( s instanceof PhysicalSensor ) {
				PhysicalSensor ps = (PhysicalSensor) s;
				String val, attrName[] = {"x", "y", "rotation"};
				double attr[] = new double[attrName.length];
				for ( int i = 0; i < attr.length; i++ ) {
					val = childSensorEl.getAttributeValue(attrName[i]);
					if ( val != null )
						attr[i] = Double.parseDouble(val);
				}
				ps.translate(attr[0], attr[1]);
				ps.rotate(attr[2]);
			}
			sensors.add(s);
		}
		return sensors;
	}
	
	protected void configBody(Element bodyEl, Body b, String onInit, String onReset, String x, String y, 
			String w, String h) {
		b.setLocation(Double.parseDouble(x), Double.parseDouble(y));
	}
}
